﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dukous.Search;
using System.Web.Mvc;

namespace Dukous.Web.Mvc
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class SearchContextModelBinder : ICustomModelBinder
    {
        private static readonly Type[] supportedTypes = new Type[] 
        {
            typeof(ISearchContext),
        };

        #region ICustomModelBinder 成员

        public IEnumerable<Type> SupportedTypes
        {
            get { return supportedTypes; }
        }

        #endregion

        #region IModelBinder 成员

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            IValueProvider valueProvider = bindingContext.ValueProvider;
            bool hasPrefix = !string.IsNullOrEmpty(bindingContext.ModelName);
            string prefix = hasPrefix ? bindingContext.ModelName + "." : string.Empty;

            IList<string> keys = controllerContext.HttpContext.Request.Params.AllKeys.Where(x =>
                x.Equals("limit", StringComparison.OrdinalIgnoreCase) ||
                x.Equals("start", StringComparison.OrdinalIgnoreCase) ||
                x.StartsWith(prefix)).ToList();

            HashSet<string> filterFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            foreach (var key in keys)
            {
                string[] parts = key.Substring(prefix.Length).Split('.');
                string fieldName = parts[0];
                if (parts.Length == 1)
                {
                    BindTermFilter(bindingContext, key, fieldName, valueProvider.GetValue(key) /*item.Value*/);
                }
                else
                {
                    string type = parts[1];
                    if (type.Equals("min", StringComparison.OrdinalIgnoreCase))
                    {
                        if (filterFields.Contains(fieldName))
                        {
                            continue;
                        }
                        filterFields.Add(fieldName);
                        ValueProviderResult maxResult = valueProvider.GetValue(string.Concat(prefix, fieldName, ".max"));
                        BindRangeFilter(bindingContext, prefix, fieldName, valueProvider.GetValue(key), maxResult);
                    }
                    else if (type.Equals("max", StringComparison.OrdinalIgnoreCase))
                    {
                        if (filterFields.Contains(fieldName))
                        {
                            continue;
                        }
                        filterFields.Add(fieldName);
                        ValueProviderResult minResult = valueProvider.GetValue(string.Concat(prefix, fieldName, ".min"));
                        BindRangeFilter(bindingContext, prefix, fieldName, minResult, valueProvider.GetValue(key));
                    }
                }
            }

            return bindingContext.Model;
        }

        private void BindRangeFilter(ModelBindingContext bindingContext, string prefix, string fieldName, ValueProviderResult minResult, ValueProviderResult maxResult)
        {
            if (minResult == null)
            {
                minResult = new ValueProviderResult(string.Empty, string.Empty, null);
            }
            if (maxResult == null)
            {
                maxResult = new ValueProviderResult(string.Empty, string.Empty, null);
            }
            bindingContext.ModelState.SetModelValue(prefix + fieldName + ".min", minResult);
            bindingContext.ModelState.SetModelValue(prefix + fieldName + ".max", maxResult);

            ISearchContext searchContext = (ISearchContext)bindingContext.Model;
            searchContext.SetFilter(fieldName, minResult.AttemptedValue, maxResult.AttemptedValue);
        }

        private void BindTermFilter(ModelBindingContext bindingContext, string key, string fieldName, ValueProviderResult valueProviderResult)
        {
            if (!string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
            {
                ISearchContext searchContext = (ISearchContext)bindingContext.Model;
                searchContext.SetFilter(fieldName, valueProviderResult.AttemptedValue);
            }
        }

        #endregion
    }
}
